﻿using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using CPF.Controls;
using CPF.Drawing;
using LibVLCSharp.Shared;
using CPF.Reflection;

namespace CPF.Controls
{
    /// <summary>
    /// vlc视频播放器
    /// </summary>
    public class VideoView : Control
    {
        /// <summary>
        /// MediaPlayer property for this VideoView
        /// </summary>
        [NotCpfProperty]
        [Browsable(false)]
        public MediaPlayer MediaPlayer
        {
            get;
            set;
        }
        static LibVLC libVLC;
        [Browsable(false)]
        public static LibVLC LibVLC
        {
            get
            {
                if (libVLC == null)
                {
                    Core.Initialize();
                    libVLC = new LibVLC();
                }
                return libVLC;
            }
        }

        IntPtr m_videoBuffer;
        uint m_videoWidth = 640;
        uint m_videoHeight = 360;
        PixelSize? newSize;
        //object m_lockObj = new object();
        /// <summary>
        /// 视频渲染尺寸，下次播放视频时生效。如果不设置则是自动尺寸
        /// </summary>
        [Description("视频渲染尺寸，下次播放视频时生效。如果不设置则是自动尺寸")]
        public PixelSize VideoRenderSize
        {
            get { return GetValue<PixelSize>(); }
            set { SetValue(value); }
        }

        protected override void OnInitialized()
        {
            if (!DesignMode)
            {
                m_videoBuffer = Marshal.AllocHGlobal((int)(m_videoWidth * m_videoHeight * 4));
                using (Bitmap bmp = new Bitmap((int)m_videoWidth, (int)m_videoHeight, (int)m_videoWidth * 4, PixelFormat.Bgra, m_videoBuffer))
                {
                    using (var dc = DrawingContext.FromBitmap(bmp))
                    {
                        dc.Clear(Color.Transparent);
                    }
                }
                MediaPlayer = new MediaPlayer(LibVLC);
                //MediaPlayer.SetVideoFormatCallbacks(VideoFormat, CleanupVideo);
                var rs = VideoRenderSize;
                if (rs.Width > 0 && rs.Height > 0)
                {
                    m_videoWidth = (uint)rs.Width;
                    m_videoHeight = (uint)rs.Height;
                }
                MediaPlayer.SetVideoFormat("RV32", m_videoWidth, m_videoHeight, m_videoWidth * 4);
                MediaPlayer.SetVideoCallbacks(libvlc_video_lock, libvlc_video_unlock, libvlc_video_display);
                MediaPlayer.Opening += MediaPlayer_Opening;

            }
            base.OnInitialized();
        }

        private void MediaPlayer_Opening(object sender, EventArgs e)
        {
            Invoke(() =>
            {
                if (newSize.HasValue)
                {
                    if (m_videoBuffer != IntPtr.Zero)
                    {
                        Marshal.FreeHGlobal(m_videoBuffer);
                    }
                    m_videoWidth = (uint)newSize.Value.Width;
                    m_videoHeight = (uint)newSize.Value.Height;
                    m_videoBuffer = Marshal.AllocHGlobal((int)(m_videoWidth * m_videoHeight * 4));
                    newSize = null;
                    MediaPlayer.SetVideoFormat("RV32", m_videoWidth, m_videoHeight, m_videoWidth * 4);
                }
            });
        }

        ///// <summary>
        ///// Called by Vlc when it requires a cleanup
        ///// </summary>
        ///// <param name="userdata">The parameter is not used</param>
        //private void CleanupVideo(ref IntPtr userdata)
        //{
        //    // This callback may be called by Dispose in the Dispatcher thread, in which case it deadlocks if we call RemoveVideo again in the same thread.
        //}

        //private uint VideoFormat(ref IntPtr opaque, IntPtr chroma, ref uint width,
        //    ref uint height, ref uint pitches, ref uint lines)
        //{
        //    TextWriteTo("RV32", chroma);
        //    Invoke(() =>
        //    {
        //        if (newSize.HasValue)
        //        {
        //            if (m_videoBuffer != IntPtr.Zero)
        //            {
        //                Marshal.FreeHGlobal(m_videoBuffer);
        //            }
        //            m_videoWidth = (uint)newSize.Value.Width;
        //            m_videoHeight = (uint)newSize.Value.Height;
        //            m_videoBuffer = Marshal.AllocHGlobal((int)(m_videoWidth * m_videoHeight * 4));
        //            newSize = null;
        //        }
        //    });
        //    width = m_videoWidth;
        //    height = m_videoHeight;
        //    //lines = m_videoHeight;
        //    //pitches = width * 4;
        //    pitches = this.GetAlignedDimension((uint)(width * 4) / 8, 32);
        //    lines = this.GetAlignedDimension(height, 32);
        //    return 1;
        //}
        //private uint GetAlignedDimension(uint dimension, uint mod)
        //{
        //    var modResult = dimension % mod;
        //    if (modResult == 0)
        //    {
        //        return dimension;
        //    }

        //    return dimension + mod - (dimension % mod);
        //}

        private IntPtr libvlc_video_lock(IntPtr opaque, IntPtr planes)
        {
            //Monitor.Enter(m_lockObj);
            if (Root != null && !IsDisposing && !IsDisposed)
            {
                BeginInvoke(Invalidate);
            }
            Marshal.WriteIntPtr(planes, m_videoBuffer);
            return IntPtr.Zero;
        }
        void libvlc_video_unlock(IntPtr opaque, IntPtr picture, IntPtr planes)
        {

            //Monitor.Exit(m_lockObj);
        }

        private void libvlc_video_display(IntPtr opaque, IntPtr picture)
        {
        }

        public void Play(Uri uri = null)
        {
            if (uri == null)
            {
                MediaPlayer.Play();
            }
            else
            {
                if (MediaPlayer.IsPlaying)
                {
                    new Thread(() =>
                    {
                        MediaPlayer.Stop();
                        MediaPlayer.Play(new Media(libVLC, uri));
                    })
                    { Name = "停止并播放新的视频", IsBackground = true }.Start();
                }
                else
                {
                    MediaPlayer.Play(new Media(libVLC, uri));
                }
            }
        }

        public void Pause()
        {
            MediaPlayer.Pause();
        }

        public void Stop()
        {
            new Thread(() =>
            {
                if ((bool)MediaPlayer.GetType().GetField("IsDisposed", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance).GetValue(MediaPlayer))
                {
                    return;
                }
                MediaPlayer.Stop();
            })
            { Name = "停止播放", IsBackground = true }.Start();
        }

        protected override void OnRender(DrawingContext dc)
        {
            base.OnRender(dc);
            if (m_videoBuffer == IntPtr.Zero)
            {
                return;
            }
            using (Bitmap bmp = new Bitmap((int)m_videoWidth, (int)m_videoHeight, (int)m_videoWidth * 4, PixelFormat.Bgra, m_videoBuffer))
            {
                dc.DrawImage(bmp, new Rect(new Point(), ActualSize), new Rect(0, 0, (int)m_videoWidth, (int)m_videoHeight));
            }
        }
        [PropertyChanged(nameof(VideoRenderSize))]
        void OnVideoRenderSize(object newValue, object oldValue, PropertyMetadataAttribute attribute)
        {
            var rs = (PixelSize)newValue;
            if (rs.Width > 0 && rs.Height > 0)
            {
                newSize = rs;
            }
            else
            {
                newSize = null;
            }
        }

        protected override void OnLayoutUpdated()
        {
            base.OnLayoutUpdated();
            var rs = VideoRenderSize;
            if (rs.Width <= 0 || rs.Height <= 0)
            {
                if (!IsDisposed && !IsDisposing && ActualSize != new Size(m_videoWidth, m_videoWidth) / Root.RenderScaling)
                {
                    var max = Root.Screen.Bounds.Size;
                    var w = Math.Min(ActualSize.Width * Root.RenderScaling, max.Width);
                    var h = Math.Min(ActualSize.Height * Root.RenderScaling, max.Height);
                    if ((int)w != m_videoWidth || (int)h != m_videoHeight)
                    {
                        newSize = new PixelSize((int)w, (int)h);
                        //if (MediaPlayer != null)
                        //{
                        //    MediaPlayer.SetVideoFormat("RV32", (uint)w, (uint)h, (uint)w * 4);
                        //}
                    }
                }
            }
        }

        public static void TextWriteTo(string fourCCString, IntPtr destination)
        {
            if (fourCCString.Length != 4)
            {
                throw new ArgumentException("4CC codes must be 4 characters long", nameof(fourCCString));
            }

            var bytes = Encoding.ASCII.GetBytes(fourCCString);

            for (var i = 0; i < 4; i++)
            {
                Marshal.WriteByte(destination, i, bytes[i]);
            }
        }


        protected override void Dispose(bool disposing)
        {
            var m = MediaPlayer;
            if (m != null)
            {
                new Thread(() =>
                {
                    m.Dispose();
                    if (m_videoBuffer != IntPtr.Zero)
                    {
                        Marshal.FreeHGlobal(m_videoBuffer);
                        m_videoBuffer = IntPtr.Zero;
                    }
                })
                { Name = "关闭播放器", IsBackground = true }.Start();
                //libVLC.Dispose();
            }
            base.Dispose(disposing);
        }
    }
}
